package com.navinfo.platform.trip.analysis.service;

import com.navinfo.location.mileage.bean.MileageAndFuel;
import com.navinfo.platform.trip.analysis.pojo.PreTripInfo;
import com.navinfo.platform.trip.analysis.pojo.TripJudgeState;
import com.navinfo.platform.trip.analysis.util.TripStatisticUtils;
import com.navinfo.platform.trip.common.arithmetic.ParkingPowerOn;
import com.navinfo.platform.trip.common.arithmetic.data.DrivingData;
import com.navinfo.platform.trip.common.arithmetic.var.VarParkingPowerOn;
import com.navinfo.platform.trip.common.enums.ProtocolEnum;
import com.navinfo.platform.trip.common.enums.SystemTypeEnum;
import com.navinfo.platform.trip.common.enums.TripStatusEnum;
import com.navinfo.platform.trip.common.pojo.PointProtocol;
import com.navinfo.platform.trip.common.util.ArithmeticUtils;
import com.navinfo.platform.trip.common.util.CommonUtils;

import java.io.Serializable;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
 * 多车厂实现：行程切分服务
 * @author: web
 * @data: 2020-04-12
 */
public interface TripJudgeService extends Serializable {
    /**
     * 强制结束行程时长阀值：10分钟
     * 单位:MS
     */
    long FORCE_END_DURATION = 10 * 60 * 1000L;

    /**
     * 获取系统类型
     */
    SystemTypeEnum systemType();

    /**
     * 判断当前行程状态
     * @param tripJudgeState 行程状态判断的缓存
     * @param point 当前点
     * @return 当前车辆行程状态
     */
    TripStatusEnum tripStatus(TripJudgeState tripJudgeState, PointProtocol point);

    /**
     * 根据0200切分行程
     * 1）根据0200点，切分出子行程，设置行程开始、结束信息；<br/>
     * 2）添加行程内的0200和0F37点集合;<br/>
     * 3）根据0200的子行程调用精准里程油耗算法算出子行程的精准里程、精准油耗<br/>
     * 4）处理行程外的统计信息，目前支持停车通电算法
     * @param points 0f37和0200混合点
     * @param drivingData 算法链的缓存信息
     * @return 行程列表
     */
    default List<PreTripInfo> judgeTrip(List<PointProtocol> points, DrivingData drivingData) {
        List<PreTripInfo> tripInfoList = new ArrayList<>();

        if (CommonUtils.isCollectionNotEmpty(points)) {
            PreTripInfo preTripInfo = null;
            TripJudgeState tripJudgeState = null;

            for (PointProtocol point: points) {
                ProtocolEnum protocol = point.getProtocol();

                //根据0200点切分行程
                if(ProtocolEnum.LOCATION.equals(protocol)){
                    switch (tripStatus(tripJudgeState, point)){
                        case TRIP_NO_START:
                            //行程外统计信息处理
                            if(null == preTripInfo){
                                //停车通电算法处理
                                Object obj = drivingData.getStatisticData().get(ParkingPowerOn.class.getSimpleName());
                                if(obj instanceof VarParkingPowerOn){
                                    VarParkingPowerOn var = (VarParkingPowerOn)obj;
                                    long gpsTime = point.getGpsTime();

                                    //上一个行程设置了停车时间
                                    if(var.getParkingTime()>0 && gpsTime>var.getParkingTime()){
                                        int rotation = point.getRotation();
                                        int accStatus = point.getAccStatus();

                                        //ACC开且上次ACC也为开
                                        if (accStatus==1 && var.isLastAccStatus() && rotation<500) {
                                            long duration = gpsTime - var.getLastGpsTime();
                                            if (duration > 0) {
                                                var.setParkingPowerOnTime((int)(var.getParkingPowerOnTime() + duration));
                                            }
                                        }

                                        var.setLastGpsTime(gpsTime);
                                        var.setLastAccStatus(accStatus==1 && rotation<500);
                                    }
                                }
                            }
                            break;
                        case TRIP_START:
                        case TRIP_IN:
                            //初始化行程
                            preTripInfo = null==preTripInfo ? handleStart(point, drivingData) : preTripInfo;

                            //加入0200点到行程信息中
                            preTripInfo.getPoints0200().add(point);
                            preTripInfo.getPoints0F37().add(point);

                            //更新行程状态判断的缓存数据
                            tripJudgeState = Optional.ofNullable(tripJudgeState).orElse(new TripJudgeState());
                            Long lastGpsTime = tripJudgeState.getLastGpsTime();
                            tripJudgeState.setLowRotationDuration(null==lastGpsTime ? 0 : (tripJudgeState.getLastRotation()<=500 && point.getRotation()<=500 ?
                                    tripJudgeState.getLowRotationDuration()+(point.getGpsTime()-lastGpsTime) : 0)) ;
                            tripJudgeState.addCount0200(1);
                            tripJudgeState.setLastGpsTime(point.getGpsTime());
                            tripJudgeState.setLastRotation(point.getRotation());

                            break;
                        default:
                            //行程结束或强制结束处理
                            if (null != preTripInfo) {
                                preTripInfo.getPoints0200().add(point);
                                preTripInfo.getPoints0F37().add(point);
                                handleEnd(tripInfoList, preTripInfo);

                                //更新行程统计的中间缓存
                                drivingData.setLastTripID(preTripInfo.getTripId());
                                drivingData.setLastTripDay(LocalDateTime.ofInstant(Instant.ofEpochMilli(preTripInfo.getStartTime()),ZoneId.systemDefault()));

                                //行程外统计信息处理，目前只有停车通电算法
                                VarParkingPowerOn varParkingPowerOn = new VarParkingPowerOn();
                                varParkingPowerOn.setParkingTime(preTripInfo.getEndTime());
                                drivingData.setStatisticData(ParkingPowerOn.class.getSimpleName(), varParkingPowerOn);
                            }

                            //清空状态
                            preTripInfo = null;
                            tripJudgeState = null;
                            break;
                    }
                }

                //加入行程内的0F37点
                else if(ProtocolEnum.REALTIME.equals(protocol)){
                    if(null != preTripInfo){
                        preTripInfo.getPoints0F37().add(point);
                    }
                }
            }
        }

        return tripInfoList;
    }

    /**
     * 行程开始处理
     * @param pojo 0200位置点
     * @param drivingData 算法链的缓存信息
     * @return 行程信息
     */
    default PreTripInfo handleStart(PointProtocol pojo ,DrivingData drivingData){
        long gpsTime = pojo.getGpsTime();

        LocalDateTime currTripDay = LocalDateTime.ofInstant(Instant.ofEpochMilli(gpsTime), ZoneId.systemDefault());
        LocalDateTime lastTripDay = Optional.ofNullable(drivingData.getLastTripDay()).orElse(currTripDay);
        Integer lastTripID = currTripDay.getDayOfYear()!=lastTripDay.getDayOfYear() ? 0 : Optional.ofNullable(drivingData.getLastTripID()).orElse(0);

        PreTripInfo preTripInfo = new PreTripInfo();
        preTripInfo.setTripId(lastTripID+1);
        preTripInfo.setStartTime(gpsTime);
        preTripInfo.setStartLongitude(pojo.getOriginalLng());
        preTripInfo.setStartLatitude(pojo.getOriginalLat());
        preTripInfo.setStartHeight((int)pojo.getHeight());
        preTripInfo.setStartDirection(pojo.getDirection());
        return preTripInfo;
    }

    /**
     * 行程结束的处理
     * @param list 行程列表
     * @param preTripInfo 当前行程
     */
    default void handleEnd(List<PreTripInfo> list, PreTripInfo preTripInfo) {
        //没有行程信息直接返回
        if (null==list || null==preTripInfo) {
            return;
        }

        //最后一个0200点的gps时间（最大）
        List<PointProtocol> points0200 = preTripInfo.getPoints0200();
        if(CommonUtils.isCollectionNotEmpty(points0200)){
            PointProtocol last0200 = points0200.get(points0200.size() - 1);
            preTripInfo.setEndTime(last0200.getGpsTime());
            preTripInfo.setEndLatitude(last0200.getOriginalLat());
            preTripInfo.setEndLongitude(last0200.getOriginalLng());
            preTripInfo.setEndHeight((int)last0200.getHeight());
            preTripInfo.setEndDirection(last0200.getDirection());

            //精准里程油耗
            MileageAndFuel mileageAndFuel = TripStatisticUtils.calcMileageFuel(systemType(), points0200);
            preTripInfo.setPreciseFuel(ArithmeticUtils.fuelLToMl(mileageAndFuel.getStdFuelCon()));
            preTripInfo.setPreciseMileage(ArithmeticUtils.mileageKMToM(mileageAndFuel.getStdMileage()));

            //加入行程列表
            list.add(preTripInfo);
        }
    }

    /**
     * 当前时间-上一个时间超过指定阀值，强制结束行程
     * @param lastGpsTime 上一个Point时间点
     * @param gpsTime     当前Point时间点
     * @return 是否强制结束
     */
    default boolean isForceEnd(long lastGpsTime, long gpsTime) {
        return (gpsTime - lastGpsTime) > FORCE_END_DURATION;
    }

}
